盒子
盒子
文章目录
  1. 微信授权部分
  2. 实时聊天部分
  3. 基本会议功能
  4. 活动部分功能

用 Node.js 和 express 完成的实时会议系统微信公众号后台

之前和小伙伴完成的一个公众号,记录一下大概的开发过程吧

微信授权部分

我们用的是企业微信号,因为当时企业微信的官方文档更新不够及时,有部分 api 的请求参数是错误的,所以耽误了不少时间。

第一步
请求微信的api首先需要拿到一个 accessToken,而这个 token 是通过 corpid 和 corpsecret 来拿到的

corpid:每个企业都拥有唯一的 corpid,获取此信息可在管理后台“我的企业”-“企业信息”下查看(需要有管理员权限)
userid: 每个成员都有唯一的 userid,即所谓“帐号”。在管理后台->“通讯录”->点进某个成员的详情页,可以看到

这个 token 的过期时间默认是7200s,保存 token 的方法有很多种,比如说使用 redis,或者直接放在文件中,我这使用的是 guard_dog

1
npm install guard_dog --save
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
const guard_dog = require('guard_dog')
guard_dog.init('ACCESS_TOKEN', (handler) => {
request.get({
uri: 'https://qyapi.weixin.qq.com/cgi-bin/gettoken?',
json: true,
qs: {
'corpid': config.corpId,
'corpsecret': config.corpSecret
}
}, (err, res, body) => {
if (err) {
console.log(err)
return
}
if (body.errcode) {
return
}
handler(body.access_token, body.expires_in)
})
})

第二步
配置可信域名,具体参考官方文档
配置完可信域名后,需要构造如下的连接来获得 code 参数
https://open.weixin.qq.com/connect/oauth2/authorize?appid=CORPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&agentid=AGENTID&state=STATE#wechat_redirect
用户点击后,页面将跳转至 redirect_uri?code=CODE&state=STATE

第三步
获得 code 参数后,就可以根据 code 获取用户信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
guard_dog.get('ACCESS_TOKEN', (data) => {
const tokenParams = {
'access_token': data,
'code': code
}
let tokenUrl = 'https://qyapi.weixin.qq.com/cgi-bin/user/getuserinfo?' + qs.stringify(tokenParams)
request(tokenUrl, function (err, response, body) {
let userTicket = body.split('"')[17]
const userParams = {
'access_token': data
}
let userUrl = 'https://qyapi.weixin.qq.com/cgi-bin/user/getuserdetail?' + qs.stringify(userParams)
let options = {
url: userUrl,
method: "POST",
json: true,
headers: {
"content-type": "application/json",
},
body: {
"user_ticket": userTicket
}
}
request(options, function (err, response, bodyData) {
getBody(bodyData)
.then(user => {
res.json({
status: 200,
user
})
})
})
})
})

实时聊天部分

主要使用了 socket 来实现

1
npm install socket.io --save
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const socket = require('socket.io')
const server = http.createServer(app).listen(4000)
const io = socket(server)
io.on('connection', function (socket) {
console.log('connected')
socket.on('joinRoom', function (room) {
socket.join(room)
socket.on('sendData', function (data) {
io.sockets.in(room).emit('receiveData', data)
})
})
})

基本会议功能

如 加入会议/创建会议等等..

大部分都是一些对数据库的增删改查,创建会议时会自动分配一个五位数的随机会议号

1
2
3
4
5
6
7
function radomNumber () {
let number = ''
for (let i = 0; i < 5; i++) {
number += Math.floor(Math.random() * 10)
}
return number
}

在这里,因为涉及到与数据库的交互,所以要注意异步问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
async number(req, res) {
let result = ''
let number = radomNumber()
while (1) {
result = await Conference.find({number: number})
if (JSON.stringify(result) === '[]') {
res.json({
number
})
return
} else {
number = radomNumber()
}
}
}

有一个扫描二维码可以进入会议的功能,实现的逻辑就是通过 url 生成一个二维码返回给前端,用到了 qrcode 这个 npm 包

1
npm install qrcode --save

1
2
3
4
5
6
7
8
9
10
const QRCode = require('qrcode')
getQRCode(req, res) {
QRCode.toDataURL(req.body.url, function (err, url) {
res.json({
status: 200,
result: url
})
})
}

活动部分功能

主要实现了四个活动功能,投票,抽奖,评分,红包

投票功能

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Vote
.update({_id: req.body._id, "options.name": req.body.name},
{$inc:{"options.$.people":1}})
.then(() => {
res.json({
status: 200,
result: 'Success!'
})
})
.catch(err => {
console.error(err)
if (err.json) {
err.message = err.json.message
}
if (!returned) {
res.status(500).json({
status: 500,
result: err.message
})
}
})

抽奖功能

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Conference
.findOne({number: req.body.number})
.then(conference => {
let winner = Math.floor(Math.random() * (conference.people))
Lottery
.update({_id: req.body._id},
{$set:{luckyDog: conference.userList[winner].name, isBegin: true}})
.then(() => {
res.json({
status: 200,
result: 'Success!'
})
})
})

评分功能

1
2
3
4
5
6
7
8
9
10
Rate
.update(
{_id:req.body._id},
{$inc: {totalScore: req.body.score, totalPeople: 1}})
.then(() => {
res.json({
status: 200,
result: 'Success!'
})
})

红包功能
因为涉及到支付功能的话需要进行企业认证,所以采用了模拟红包的方法来实现

首先是一个红包算法

1
2
3
4
5
6
7
8
9
10
11
12
function red (rpAmount, ppCount) {
let rpResult=[]
let rpRnds = []
let rpRndSum = 0
for(let i=0;i<ppCount;i++){
let rnd = Math.random();
rpRndSum += rnd;
rpRnds.push(rnd);
}
rpRnds.forEach((rnd)=>{rpResult.push(parseFloat((rpAmount*rnd/rpRndSum).toFixed(2)))})
return rpResult
}

rpAmount 是总金额,ppCount 是红包个数
以10个红包,一百元为例:
先生成10个0-1之间的随机数,加入到 rpRnds 这个数组中,然后对这个数组中的每个数都进行运算:rpAmount*rnd/rpRndSum
(rpRndSum 为这十个数的总和)
这样就可以得到较为随机的红包金额

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Red
.findOne({_id: req.body._id})
.then(red => {
if (red.index > red.people - 1) {
res.json({
money: null
})
} else {
Red
.update({_id: req.body._id},
{$push:{luckyDogs: {username:req.body.username, money:red.money[red.index]}},
$inc:{index: 1}})
.then(() => {
res.json({
money: red.money[red.index],
index: red.index
})
})
}
})